﻿using System;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Rsdn.Humor.Collector
{
  public delegate void ErrorHandlerDelegate(ResReq sender, Uri url, Exception ex);
  public delegate void CompleteHandlerDelegate(ResReq sender, Uri url);

  public abstract class ResReq
  {
    private Uri _ResUrl;
    private int _TimeOut;
    private Encoding _Encoding;

    protected ErrorHandlerDelegate _ErrorRaised;

    protected CompleteHandlerDelegate _Completed;


    private void _OnError(Exception ex)
    {
      if (null != _ErrorRaised)
        _ErrorRaised(this, _ResUrl, ex);
    }

    private void _OnComplete()
    {
      if (null != _Completed)
        _Completed(this, _ResUrl);
    }

    protected void Execute()
    {
      Execute(_ResUrl);
    }

    protected void Execute(Uri url)
    {
      try
      {
        var req = WebRequest.Create(url);
        if (_ResUrl.Scheme == Uri.UriSchemeHttp || _ResUrl.Scheme == Uri.UriSchemeHttps)
        {
          var hreq = (HttpWebRequest)req;
          hreq.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
          hreq.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 1.1.4322)";
        }
        req.Timeout = _TimeOut;
        string response;
        using (WebResponse resp = (WebResponse)req.GetResponse())
        using (StreamReader sr = new StreamReader(resp.GetResponseStream(), _Encoding))
          response = sr.ReadToEnd();
        PageParser(response);
        _OnComplete();
      }
      catch (WebException ex)
      {
        _OnError(new Exception("Ошибка загрузки.", ex));
      }
      catch (Exception ex)
      {
        _OnError(new Exception("Ошибка разбора.", ex));
      }
    }

    protected int Timeout { get { return _TimeOut; } }

    protected Encoding Encoding { get { return _Encoding; } }

    protected ResReq(string url, Encoding enc, int to, CompleteHandlerDelegate ccb, ErrorHandlerDelegate ecb)
    {
      _ResUrl = new Uri(ResReqHelper.ResDomain, url);
      _TimeOut = to;
      _Encoding = enc;
      if (null != ccb)
        _Completed += ccb;
      if (null != ecb)
        _ErrorRaised += ecb;

    }

    protected abstract void PageParser(string raw);

    public Uri URL { get { return _ResUrl; } }

/*    public event ErrorHandlerDelegate ErrorRaised
    {
      add { _ErrorRaised += value; }
      remove { _ErrorRaised -= value; }
    }

    public event CompleteHandlerDelegate Completed
    {
      add { _Completed += value; }
      remove { _Completed -= value; }
    }*/
  }

  [Serializable]
  [DebuggerDisplay("{_DebugString}")]
  public struct ResId
  {
    private string _OriginalUri;
    private Uri _FullUri;
    private int _Id;


    private string _DebugString { get { return string.Format("id:{0}, {1}", _Id, _FullUri.AbsoluteUri); } }

    public ResId(string uri)
    {
      _OriginalUri = uri;
      var netto_uri = uri.Trim('\'', '\"', ' ').TrimStart('/');
      _FullUri = new Uri(ResReqHelper.ResDomain, netto_uri);
      var m = ResReqHelper.LinkIdPattern.Match(netto_uri);
      if (!m.Success || !int.TryParse(m.Value, out _Id))
        throw new Exception("Invalid URL: " + uri);
    }

    public static implicit operator Uri(ResId value)
    {
      return value._FullUri;
    }

    public int Id { get { return _Id; } }

    public override bool Equals(object obj)
    {
      if(obj is int)
        return _Id == (int)obj;
      else if(obj is ResId)
        return _Id == ((ResId)obj)._Id;
      throw new System.ArgumentException(null != obj ? obj.ToString() : "null");
    }

    public override int GetHashCode()
    {
      return _Id;
    }

    public override string ToString()
    {
      return _FullUri.ToString();
    }
  }

  public class ListFrame : ResReq
  {
    private List<ResId> _Links = new List<ResId>();

    private static void _PoolingProc(Object state)
    {
      ((ListFrame)state).Execute();
      ResReqHelper.IncCompletedResReq();
    }

    public ListFrame(string url, Encoding enc, int to, CompleteHandlerDelegate ccb, ErrorHandlerDelegate ecb)
      : base(url, enc, to, ccb, ecb)
    {
      if (ThreadPool.QueueUserWorkItem(new WaitCallback(_PoolingProc), this))
      {
        ResReqHelper.IncAddedResReq();
      }
    }

    protected override void PageParser(string raw)
    {
      Console.WriteLine("ListFrame URL: {0}", base.URL);
      foreach (var item in ResReqHelper.LinksListPattern.Matches(raw))
      {
        try
        {
          var m = (Match)item;
          var id = ResReqHelper.GetIdFromAHref(m.Value);
          _Links.Add(id);
        }
        catch (Exception ex)
        {
          Console.Error.WriteLine(ex);
        }
      }
      foreach (var id in _Links)
      {
        var ref_id = id;
        var rate_id = string.Format("forum/RateList.aspx?mid={0}", id.Id);
        if(!ResReqHelper.Statistics.HasStatInfo(ref ref_id))
            new Raiting(ref ref_id, rate_id, base.Encoding, base.Timeout, _Completed, _ErrorRaised);
      }
    }
  }

  class Raiting : ResReq
  {
    private int _RatesCount = -1;
    private int _CurId = 1;
    private ResId _TopId;

    public Raiting(ref ResId top_id, string url, Encoding enc, int to, CompleteHandlerDelegate ccb, ErrorHandlerDelegate ecb)
      : base(url, enc, to, ccb, ecb)
    {
      _TopId = top_id;
      Execute();
    }

    protected void PageRaitings(MatchCollection ms)
    {
      System.Diagnostics.Debug.Assert(ms.Count != 0);
      var info = default(Statistics.StatInfo);
      info.Raters = ms.Count;
      foreach (var m in ms)
      {
        var rate = ((Match)m).Value;
        if (ResReqHelper.SmileRatePattern.Match(rate).Success)
          info.SmilesCount++;
        else if (ResReqHelper.MinusRatePattern.Match(rate).Success)
          info.Dissent++;
        else if (ResReqHelper.PlusRatePattern.Match(rate).Success)
          info.Assent++;
        else
        {
          var n = ResReqHelper.NumRatePattern.Match(rate);
          System.Diagnostics.Debug.Assert(n.Success);
          int r = 0;
          if (int.TryParse(ResReqHelper.LinkIdPattern.Match(n.Value).Value, out r))
            info.Rate += r;
        }
      }
      ResReqHelper.Statistics.SetStatInfo(ref _TopId, ref info);
      _CurId += ResReqHelper.DefaultItemsInLists;
      if (_CurId <= _RatesCount)
        Execute(new Uri(string.Format(@"{0}&start={1}", base.URL.AbsoluteUri, _CurId)));

    }

    protected override void PageParser(string raw)
    {
      if (_RatesCount == -1)
      {
        Console.WriteLine("Raiting URL: {0}", base.URL);

        var m = ResReqHelper.RatingListCountPlaceholderPattern.Match(raw);
        if (m.Success)
        {
          m = ResReqHelper.RatingListCountValue1Pattern.Match(m.Value);
          if (m.Success)
          {
            m = ResReqHelper.RatingListCountValue2Pattern.Match(m.Value);
            if (m.Success)
            {
              m = ResReqHelper.LinkIdPattern.Match(m.Value);
              if (m.Success && int.TryParse(m.Value, out _RatesCount))
              {
                PageRaitings(ResReqHelper.RatingListPattern.Matches(raw));
                return;
              }
            }
            //сообщение без оценки
            var info = default(Statistics.StatInfo);
            ResReqHelper.Statistics.SetStatInfo(ref _TopId, ref info);
            return;
          }
        }
        throw new Exception("Unable to parse rates count");
      }
      else
      {
        PageRaitings(ResReqHelper.RatingListPattern.Matches(raw));        
      }
    }
  }
}
